
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>

#include "soc.h"
#include "ttusb_comn.h"
#include "ttusb.h"

#include "upaket.h"





/**/
#define XFER_MAX   256

typedef void (* trans_end_cbk)( void );

typedef struct _tag_xfer_context {

    uint8_t h2drcvb[64];            /* OUT : host to device, 64 bytes */
    uint8_t d2hpad[XFER_MAX];      /*  IN : device to host, 256 bytes */

    /**/
    int  stg;       /* current stage.. */
    int  pid;       /* data stage, buffer. */
    int  max;
    int  ofs;

    /**/
    void * endp_out_ptr;
    void * endp_in_ptr;

} xfer_context;



typedef struct _tag_usbd_context {

	usbd_config_t ucfg;			/* desc, config */

	xfer_context uxfer;			/* xfer, setup proc */
	
} usbd_context_t;




extern int log_printf( const char * fmt, ... );



/* GRSTCTL */
#define AHB_MASTER_IDLE			(1ul <<31)
#define CSFTRST_DONE        	(1ul <<29)

#define TX_FIFO_FLUSH_ALL		(0x10ul << 6)
#define TX_FIFO_FLUSH       	(1ul << 5)
#define RX_FIFO_FLUSH       	(1ul << 4)

#define CORE_SOFT_RESET			(1ul <<0)

/* GINTMSK */
#define INT_RESUME				(1ul<<31)
#define INT_DISCONN				(1ul<<29)
#define INT_RESETDET			(1ul<<23)
#define INT_OUT_EP				(1ul<<19)
#define INT_IN_EP				(1ul<<18)
#define INT_ENUMDONE			(1ul<<13)
#define INT_RESET				(1ul<<12)
#define INT_SUSPEND				(1ul<<11)
#define INT_EARLY_SUSPEND		(1ul<<10)
#define INT_GOUTNakEff			(1ul<<7)
#define INT_GINNakEff			(1ul<<6)
#define INT_NP_TX_FIFO_EMPTY	(1ul<<5)
#define INT_RX_FIFO_NOT_EMPTY	(1ul<<4)
#define INT_SOF					(1ul<<3)


/* DCTL */
#define PWRONPRGDONE			(1ul<<11)
#define CGOUTNAK				(1ul<<10)
#define SGOUTNAK				(1ul<<9)
#define CGNPINNAK				(1ul<<8)
#define SOFT_DISCONNECT			(1ul<<1)


/* DWC2_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint common interrupt mask register */
/* DWC2_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */
#define INT_TXFE						(1ul<<7)
#define BACK2BACK_SETUP_RECEIVED		(1ul<<6)
#define INEPNAKEFF						(1ul<<6)
#define INTKNEPMIS						(1ul<<5)
#define INTKN_TXFEMP					(1ul<<4)
#define NON_ISO_IN_EP_TIMEOUT			(1ul<<3)
#define CTRL_OUT_EP_SETUP_PHASE_DONE	(1ul<<3)
#define CTRL_IN_EP_TIMEOUT				(1ul<<3)
#define AHB_ERROR						(1ul<<2)
#define EPDISBLD						(1ul<<1)
#define TRANSFER_DONE					(1ul<<0)


/* DIEPCTL, DOEPCTL */
#define DEPCTL_EPENA				(1ul<<31)
#define DEPCTL_EPDIS				(1ul<<30)
#define DEPCTL_BULK					(2ul<<18)
#define DEPCTL_USBACTEP				(1ul<<15)

#define DEPCTL_SetD1PID				(1ul<<29)
#define DEPCTL_SetD0PID				(1ul<<28)
#define DEPCTL_SNAK					(1ul<<27)
#define DEPCTL_CNAK					(1ul<<26)
#define DEPCTL_STALL				(1ul<<21)



static inline uintptr_t __virt_to_phys( void * ptr )
{
	return (uintptr_t)ptr;
}


// #define __phys_to_virt(x) 



/*
准备接收 host 发来的 OUT 消息.
目前都是一个 packet.
*/
void endp_out_prep_space( struct cvi_dev_out_endp * outep, void * ptr, int tsiz )
{
	uint32_t temp;

	// cache invalid.
	csi_dcache_invalid_range( (uint64_t *)ptr, tsiz );
	
	// sus=1, pkt=1, size = 8
	temp = 0x20080000 | (tsiz & 0xfff);
	outep->DOEPTSIZ = temp;
	
	temp = (uint32_t)__virt_to_phys( ptr );
	outep->DOEPDMA = temp;
	
	// EPEna = 1; MPS=0 (64bytes).
	temp = outep->DOEPCTL;
	outep->DOEPCTL = temp | DEPCTL_EPENA | DEPCTL_CNAK;
	return;
}


void endp_in_fill_trans( struct cvi_dev_in_endp * inep, void * ptr, int tsiz )
{
	uint32_t temp;

	// special, ZLP
	if ( tsiz == 0 )  {
		
		// pkt = 1,  size = 0
		inep->DIEPTSIZ = 0x80000;
		temp = inep->DIEPCTL;
		inep->DIEPCTL = temp | DEPCTL_EPENA | DEPCTL_CNAK;
		return;
	}

	// cache flush to ddr.
	csi_dcache_clean_range( (uint64_t *)ptr, tsiz );

	// pkt = n
	temp = ( tsiz + 63 ) / 64;
	inep->DIEPTSIZ = ( temp  << 19 ) | tsiz;

	temp = (uint32_t)__virt_to_phys( ptr );
	inep->DIEPDMA = temp;

	// EPEna = 1; MPS=0 
	temp = inep->DIEPCTL;
	inep->DIEPCTL = temp | DEPCTL_EPENA | DEPCTL_CNAK;
	return;
}


void endp_in_stall( struct cvi_dev_in_endp * inep )
{
	uint32_t temp;

	/**/
	temp = inep->DIEPCTL;
	inep->DIEPCTL = temp | DEPCTL_EPENA | DEPCTL_STALL;
	return;
}


/*
*/
void endp_out_setup( cvi_usb_regs_t * pusb, int idx )
{
	uint32_t temp;
	struct cvi_dev_out_endp * outep = &pusb->out_endp[idx];
	

	/* MPS = 512 */
	temp = (1ul << 22) | DEPCTL_BULK | DEPCTL_USBACTEP | 512;
	outep->DOEPCTL = temp | DEPCTL_EPENA | DEPCTL_SNAK | DEPCTL_SetD0PID;

	/* W1C, Clear */
	outep->DOEPINT = 0xFFFF;
	outep->DOEPTSIZ = 0;

	/**/
	temp = pusb->DAINTMSK;
	pusb->DAINTMSK = temp | ( 1ul << (16 + idx) );
	return;
}

/*
todo :
[25:22] : TxFNum, 临时写 1.
*/
void endp_in_setup( cvi_usb_regs_t * pusb, int idx )
{
	uint32_t temp;
	struct cvi_dev_in_endp * inep = &pusb->in_endp[idx];

	/* MPS = 512 */
	temp = (2ul << 22) | DEPCTL_BULK | DEPCTL_USBACTEP | 512;
	inep->DIEPCTL = temp | DEPCTL_SNAK | DEPCTL_SetD0PID;

	/* W1C, Clear */
	inep->DIEPINT = 0xFFFF;
	inep->DIEPTSIZ = 0;
	inep->DIEPDMA = 0;

	/**/
	temp = pusb->DAINTMSK;
	pusb->DAINTMSK = temp | ( 1ul << idx );	
	return;
}



/*
收到 SET_CONFIGURATION 请求之后, 进入 Configured state.
遍历所有的数据 endp :
	如果是 IN 方向 : 设置为 NACK, 表示当前没有 packet 上送.
	如果是 OUT 方向 : 预先准备 buffer, 等待 host 下发数据.
*/
void usbd_intf_start( usbd_context_t * pctx, cvi_usb_regs_t * pusb )
{
	int idx;
	xfer_context * pxfer = &( pctx->uxfer );
	usbd_intf_t * pifc = &( pctx->ucfg.iface[0] );

	// usbd_endp_t * pedp = &( pctx->ucfg.endps[0] );
	
	for ( int i=0; i<pifc->epcnt; i++ )  {
		// log_printf( "adr = %x\n", pifc->epadrs[i] );

		//
		idx = pifc->epadrs[i] & 0xF;

		if ( (pifc->epadrs[i] & 0xF0) == USB_DIR_IN )  {
			
			// enable and nack
			endp_in_setup( pusb, idx );
			pxfer->endp_in_ptr = NULL;

		} else {
			
			// setup for NACK
			endp_out_setup( pusb, idx );
			
			// malloc, // prepare buffer.
			pxfer->endp_out_ptr = upk_alloc();
			endp_out_prep_space( &pusb->out_endp[idx], pxfer->endp_out_ptr, 512 );
		}

	}

	return;
}


int usbd_setup_h2d_proc( usbd_context_t * pctx, usb_setup_packet_t * pkt, cvi_usb_regs_t * pusb )
{
	uint32_t temp;
	uint8_t rtype = pkt->bmRequestType;
    uint8_t req = pkt->bRequest;

    /**/    
    if ( (rtype & USB_REQ_TYPE_TYPE_MASK) != USB_REQ_TYPE_TYPE_STANDARD )  {
        /* todo: (CLASS, VENDOR) */
        // log_printf( "rtype, todo-0: %x\n", rtype );
        return 4;
    }

    if ( (rtype & USB_REQ_TYPE_RECIPIENT_MASK) != USB_REQ_TYPE_RECIPIENT_DEVICE )  {
        /* todo: ( INTERFACE, ENDPOINT ) */
        // log_printf( "rtype, todo-1: %x\n", rtype );
        return 5;
    }

    /* STANDARD, DEVICE */
    switch (req)  {
    case USB_REQUEST_SET_ADDRESS:
        
        // 
        temp = pusb->DCFG;
        pusb->DCFG = temp | ( (pkt->wValue & 0x7f) << 4 );
        
        // ZLP.
		endp_in_fill_trans( &pusb->in_endp[0], NULL, 0 );
        break;

    case USB_REQUEST_SET_CONFIGURATION:

        // ZLP.
		endp_in_fill_trans( &pusb->in_endp[0], NULL, 0 );

        // 如果 wValue = 0 表示清除当前配置, 否则进入配置状态.
		// log_printf( "set config, todo-3: %x\n", pkt->wValue );

        /* interface start work.. */
        usbd_intf_start( pctx, pusb );
        return 0;

    default:
    	// log_printf( "req, todo-2: %x\n", req );
        return 7;
    }

	return 0;
}


/* 支持长消息, 自动分片和重组;
 * H2D, D2H, 使用同一块缓存.
 */

#define CTRL_IDLE           0
#define CTRL_D2H_DATA       1
#define CTRL_D2H_STATUS     2
#define CTRL_H2D_DATA       3
#define CTRL_H2D_STATUS     4




void usbd_ep0_setup_handler( usbd_context_t * pctx, cvi_usb_regs_t * pusb )
{
	xfer_context * pxfer;
	usb_setup_packet_t * pkt;
	uint32_t temp;
	int iret;
	uint8_t rtype;
	uint16_t wlen;
	

	/* clear irq */
	temp = pusb->out_endp[0].DOEPINT;
	pusb->out_endp[0].DOEPINT = temp;
	// log_printf( "out-endp-0, int = %x\n", temp );

	temp = pusb->out_endp[0].DOEPTSIZ;
	temp = temp & 0xffff;
	// log_printf( "out-endp-0, size = %x\n", 64 - temp );


	/* STATUS, ZLP ..*/
	if ( temp == 64 )  {
		
		pxfer = &pctx->uxfer;
		pxfer->stg = CTRL_IDLE;

		// prepare setup recv.
		endp_out_prep_space( &pusb->out_endp[0], &pxfer->h2drcvb, 64 );
		return;
	}

	pxfer = &pctx->uxfer;
	pkt = (usb_setup_packet_t *)pxfer->h2drcvb;
	rtype = pkt->bmRequestType;
	wlen = pkt->wLength;


	/**/	
	if ( (rtype & 0x80) != 0 )  {

		/* Device To Host */
        iret = usbd_setup_d2h_proc( &(pctx->ucfg), pkt, pxfer->d2hpad );


        if ( iret <= 0 )  {
        	// error...
        	endp_in_stall( &pusb->in_endp[0] );
        	// log_printf( "error iret = %d, %x\n", iret, rtype );

        } else {

			pxfer->stg = CTRL_D2H_DATA;
            pxfer->pid = 1;
            pxfer->ofs = 0;

            if ( iret > wlen )  {
                pxfer->max = wlen;
            } else  {
                pxfer->max = iret;
            }

            endp_in_fill_trans( &pusb->in_endp[0], pxfer->d2hpad, pxfer->max );
        }

        // log_printf( "d2h proc req = %x, %x, %x, wlen = %u, iret = %d\n", pkt->bRequest, pkt->wValue, pkt->wIndex, wlen, iret );

	} else {

		// log_printf( "h2d, setup\n" );

		iret = usbd_setup_h2d_proc( pctx, pkt, pusb );
		if ( iret != 0 )  {
			log_printf( "%x, %x\n", pkt->bmRequestType, pkt->bRequest );
			log_printf( "%x, %x, %x\n", pkt->wValue, pkt->wIndex, pkt->wLength );
		}

	}


	// prepare setup recv.
	endp_out_prep_space( &pusb->out_endp[0], &pxfer->h2drcvb, 64 );
	return;
}



void usbd_ep0_in_handler( usbd_context_t * pctx, cvi_usb_regs_t * pusb )
{
	uint32_t temp;

	/* clear irq */
	temp = pusb->in_endp[0].DIEPINT;
	pusb->in_endp[0].DIEPINT = temp;
	// log_printf( "in-endp-0, int = %x\n", temp );

	/**/
	return;
}


int umsg_inp_cbk( void * parg, uint8_t * ptr, int tmax )
{
	return 0;
}


int umsg_outp_cbk( void * parg, uint8_t * ptr, int tsiz )
{
	return 0;
}


static void cvi_sleep( uint32_t ticks )
{
	for ( uint32_t i=0; i<ticks; i++ )  {
		asm volatile ( "nop" );
	}
}




uint64_t wymix( uint64_t uaa, uint64_t ubb )
{
	uint64_t tlow;
	uint64_t high;

	tlow = uaa * ubb;
	__asm__ __volatile__( "mulhu  %0, %1, %2" : "=r" (high) : "%r" (uaa), "r" (ubb) );

	return tlow ^ high;
}


uint64_t m1 = 0xa0761d6478bd642full;
uint64_t m2 = 0xe7037ed1a0b428dbull;
uint64_t m3 = 0x8ebc6af09c88c6e3ull;
uint64_t m4 = 0x589965cc75374cc3ull;
uint64_t m5 = 0x1d8e4e27c47d124full;

/*
ref :
range : 
0xEFFFF 范围对应一个 6 位的 10 进制的数据.
如果转成字符串, 对于使用者 口述传递信息 更方便.
*/
uint32_t wyhash( uint64_t uaa, uint64_t ubb )
{
	uint64_t temp;

	uaa = uaa ^ m1;
	ubb = ubb ^ m2;

	temp = wymix( uaa, ubb );
	temp = wymix( m5, temp );

	// 缩减到 32 bits.
	uaa = temp & 0xFFFFFFFF;
	temp = temp >> 32;
	temp = wymix( temp, uaa );

	return (uint32_t)( temp ^ (temp >> 19) );
}


char * qc_uid_str( void )
{
	uintptr_t efuse = 0x3050000;
	uint64_t uaa;
	uint64_t ubb;
	uint64_t tmp;
	uint32_t rult;
	char * ptr;

	uaa = read_reg( efuse + 0x108 );
	ubb = read_reg( efuse + 0x10C );
	tmp = read_reg( efuse + 0x124 );

	uaa = (uaa << 16) | (tmp & 0xFFFF);
	ubb = (ubb << 16) | ( ( tmp >> 16) & 0xFFFF );

	rult = wyhash( uaa, ubb );

	ptr = malloc( 16 );
	if ( ptr == NULL )  {
		return "123456";
	}

	sprintf( ptr, "%06u", rult & 0xEffff );	
	return ptr;
}



int bl2_dbg_uid( void * pctx, int argc, const char * argv[] )
{
	uintptr_t efuse = 0x3050000;
	uint64_t uaa;
	uint64_t ubb;
	uint64_t tmp;
	uint32_t rult;
	
	uaa = read_reg( efuse + 0x108 );
	ubb = read_reg( efuse + 0x10C );
	tmp = read_reg( efuse + 0x124 );
	printf( "uaa = %08x\n", (uint32_t)uaa );
	printf( "ubb = %08x\n", (uint32_t)ubb );
	printf( "tmp = %08x\n", (uint32_t)tmp );


	uaa = (uaa << 16) | (tmp & 0xFFFF);
	ubb = (ubb << 16) | ( ( tmp >> 16) & 0xFFFF );

	rult = wyhash( uaa, ubb );
	printf( "rult = %08x, %u\n", rult, rult );
	return 0;
}



usbd_context_t uctx;


int bl2_dbg_usb_init( void * pctx, int argc, const char * argv[] )
{
	uint32_t temp;
	cvi_usb_regs_t * pusb = (cvi_usb_regs_t *)0x4340000;
	usbd_config_t * pcfg = &(uctx.ucfg);

	/* ver = 420A : core reset */
	pusb->GRSTCTL = CORE_SOFT_RESET;

	while ( true )  {

		temp = pusb->GRSTCTL;
		if ( (temp & CSFTRST_DONE) != 0 )  {
			break;
		}

		cvi_sleep( 10 );
	}

	temp = pusb->GRSTCTL;
	temp = temp ^ CORE_SOFT_RESET;
	temp = temp | CSFTRST_DONE;
	pusb->GRSTCTL = temp;


	/* wait ahb idle */
	while ( true )  {
		temp = pusb->GRSTCTL;

		if ( temp & AHB_MASTER_IDLE )  {
			break;
		}

		cvi_sleep( 10 );
	}

	// usbcfg, ForceDevMode
	pusb->GUSBCFG = 0x40081408;
	
	// PTXFE=half, NPTXFE=half, DMAEn=1, HBstLen=3(INCR4), GlblIntrMsk=1
	pusb->GAHBCFG = 0x27;

	// fifo config : totalSize = 0xC00,  used = 0xA40
	pusb->GRXFSIZ = 1024;
	temp = 1024;
	
	// tx-fifo : [0]
	pusb->GNPTXFSIZ = (64ul << 16) | temp;
	temp = temp + 64;

	// tx-fifo : [1] ... [6]
	for ( int i=0; i<6; i++ )  {
		pusb->DIEPTXF[i] = (200ul << 16) | temp;
		temp = temp + 200;
	}
	
	// flush all fifo.
	pusb->GRSTCTL = TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH | RX_FIFO_FLUSH;
	while ( true )  {
		temp = pusb->GRSTCTL;
		if ( (temp & (TX_FIFO_FLUSH | RX_FIFO_FLUSH)) == 0 )  {
			break;
		}

		cvi_sleep( 10 );
	}
	
	
	/* SoftDiscon */
	temp = pusb->DCTL;
	pusb->DCTL = temp | SOFT_DISCONNECT;
	
	/* DescDMA=0, EPMisCnt=1, DevSpd=0(USBHS20) */
	pusb->DCFG = 0x40000;
	
	/* clear pending interrupts */
	pusb->GOTGINT = 0xffffFFFF;
	pusb->GINTSTS = 0xffffFFFF;
	
	// Unmask the core interrupts
	pusb->GINTMSK = INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE | INT_RESET | INT_SUSPEND;
	
	// PTXFE=half, NPTXFE=half, DMAEn=1, HBstLen=3(INCR4), GlblIntrMsk=1
	pusb->GAHBCFG = 0x27;
		
	/**/
	pusb->DIEPMSK = NON_ISO_IN_EP_TIMEOUT | AHB_ERROR | TRANSFER_DONE;
	pusb->DOEPMSK = CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR | TRANSFER_DONE;
	pusb->DAINTMSK = 0x10001;

	/* PWROnPrgDone */
	temp = pusb->DCTL;
	pusb->DCTL = temp | PWRONPRGDONE;
	cvi_sleep( 100 );
	pusb->DCTL = temp;

	/* soft prepare ..*/

	// enable, but don't activate EP0in.
	pusb->in_endp[0].DIEPCTL = DEPCTL_USBACTEP;

	// clear global NAKs
	temp = pusb->DCTL;
	pusb->DCTL = temp | CGOUTNAK | CGNPINNAK;
	cvi_sleep( 100 );

	// set device config.
	ttusb_desc_init( pcfg );
	ttusb_desc_device_vid_pid( pcfg, 0x2E8A, 0x1A25 );
	ttusb_desc_device_str_manu( pcfg, "TTUsb" );
	ttusb_desc_device_str_prod( pcfg, "wise-1" );
	ttusb_desc_device_str_seri( pcfg, qc_uid_str() );
	ttusb_desc_config_power( pcfg, 100 );
	ttusb_desc_interface_add_inp( pcfg, umsg_inp_cbk );
	ttusb_desc_interface_add_outp( pcfg, umsg_outp_cbk );
	
	// clear SoftDiscon
	temp = pusb->DCTL;
	pusb->DCTL = temp & (~SOFT_DISCONNECT);

	// plic irq enable
	csi_vic_set_prio( 30, 3 );
	csi_vic_enable_irq( 30 );	
	return 0;
}




void endp_in_fill_pkt( struct cvi_dev_in_endp * inep, void * ptr, int tsiz )
{
	uint32_t temp;

	// special, ZLP
	if ( tsiz == 0 )  {
		
		// pkt = 1,  size = 0
		inep->DIEPTSIZ = 0x80000;
		temp = inep->DIEPCTL;
		inep->DIEPCTL = temp | DEPCTL_EPENA | DEPCTL_CNAK;
		return;
	}

	// 
	temp = (uint32_t)__virt_to_phys( ptr );
	inep->DIEPDMA = temp;

	// pktcnt = 1, 
	inep->DIEPTSIZ = ( 0x1ul << 19 ) | tsiz;

	// EPEna = 1; MPS=512 
	temp = inep->DIEPCTL & 0x3FFFFFFF;
	inep->DIEPCTL = temp | DEPCTL_EPENA;
	inep->DIEPCTL = temp | DEPCTL_CNAK;
	return;
}



void usbd_int_handler( void )
{
	int iret;
	uint32_t temp;
	uint32_t ints;
	uint32_t mask;
	cvi_usb_regs_t * pusb = (cvi_usb_regs_t *)0x4340000;
	
	void * ptr;
	int tlen;
	xfer_context * pxfer = &( uctx.uxfer );


	// check IRQ
	mask = pusb->GINTMSK;
	ints = pusb->GINTSTS;
		
	if ( (ints & mask) != 0 )  {
			
		// clear
		pusb->GINTSTS = ints;

		// log_printf( "int status = %x\n", (ints & mask) );


		if ( ints & INT_ENUMDONE )  {
				
			temp = pusb->DSTS;
			// log_printf( "enum done = %x\n", temp );

			// prepare setup recv.
			endp_out_prep_space( &pusb->out_endp[0], &uctx.uxfer.h2drcvb, 64 );
		}


		if ( ints & INT_OUT_EP )  {

			// xxx
			temp = pusb->DAINT;
			temp = temp >> 16;
			// log_printf( "OUT ep int = %x\n", temp );

			if ( temp & 0x1 )  {
				usbd_ep0_setup_handler( &uctx, pusb );
			} else {
				

				// out endp, idx = 2

				/* clear irq */
				temp = pusb->out_endp[2].DOEPINT;
				pusb->out_endp[2].DOEPINT = temp;
				// log_printf( "out-endp-2, int = %x\n", temp );

				temp = pusb->out_endp[2].DOEPTSIZ;
				temp = temp & 0xffff;
				// log_printf( "out-endp-0, size = %x\n", 512 - temp );

				/**/
				ptr = upk_alloc();
				if ( ptr != NULL )  {

					endp_out_prep_space( &pusb->out_endp[2], ptr, 512 );
					upk_send_to_app( pxfer->endp_out_ptr, 512-temp );
					pxfer->endp_out_ptr = ptr;

				} else {
					// fail...
					endp_out_prep_space( &pusb->out_endp[2], pxfer->endp_out_ptr, 512 );
					// log_printf( "pkt alloc fail\n" );
				}

			}
		}

		if ( ints & INT_IN_EP )  {

			temp = pusb->DAINT;
			temp = temp & 0xFFFF;
			// log_printf( "IN ep int = %x\n", temp );

			if ( temp & 0x1 )  {
				usbd_ep0_in_handler( &uctx, pusb );
			} else {

				// clear int flag.
				temp = pusb->in_endp[1].DIEPINT;
				pusb->in_endp[1].DIEPINT = temp;

				// other endp
				// log_printf( "todo, endp-x IN-int : %x \n", temp );

				if ( (temp & 0x1) != 0 )  {

					iret = upk_recv_from_app( &ptr, &tlen );
					if ( iret == 0 )  {
						
						endp_in_fill_pkt( &(pusb->in_endp[1]), ptr, tlen );
						upk_free( pxfer->endp_in_ptr );
						pxfer->endp_in_ptr = ptr;

					} else {
						
						pusb->in_endp[1].DIEPDMA = 0;
						upk_free( pxfer->endp_in_ptr );
						pxfer->endp_in_ptr = NULL;
					}
				}

			}
		}
	}
	
}



void usbd_try_send( void * ptr, int tlen )
{
	cvi_usb_regs_t * pusb = (cvi_usb_regs_t *)0x4340000;
	xfer_context * pxfer = &( uctx.uxfer );

	// cache flush to ddr.
	csi_dcache_clean_range( (uint64_t *)ptr, tlen );

	// check idle ?? 
	if ( pxfer->endp_in_ptr  != 0 )  {
		// push to soft fifo.
		upk_send_to_dev( ptr, tlen );
		return;
	}

	//
	endp_in_fill_pkt( &(pusb->in_endp[1]), ptr, tlen );
	pxfer->endp_in_ptr = ptr;
	return;
}

